#ifndef DIO_REQUEST_H_
#define DIO_REQUEST_H_

#include <fermat/common/shared_state.h>
#include <cassert>
#include <memory>
#include <mutex>
#include <string>

namespace fermat {

class Request;
class FileBase;

typedef std::shared_ptr<Request> RequestPtr;

typedef std::shared_ptr<FileBase> FileBasePtr;

typedef std::function<void(Request*, bool)> CompletionHandler;

class Request : public std::enable_shared_from_this<Request> {
public:
    typedef size_t offset_type;
    typedef size_t  size_type;

    enum ReadOrWriteType {
                READ, 
                WRITE 
        };

public:
    /******************************************************
    *constructor for a request
    * @param file file ptr for any type file file abstracted by FileBase.
    * @param buffer IObuffer.
    * @offset the offset of the file.
    * @bytes io size ,ensure that buffer has enough space.
    * @type  io type READ or WRITE
    ********************************************************/ 
    Request(const CompletionHandler& on_complete,
            const FileBasePtr& file,
            void* buffer, 
            offset_type offset, 
            size_type bytes,
            ReadOrWriteType type);

    virtual ~Request();

public:
    const FileBasePtr& file() const;
    void * buffer() const;
    offset_type offset() const;
    size_type bytes() const;
    size_type donebytes() const;
    ReadOrWriteType type() const;

    void check_alignment() const;

    std::ostream& print(std::ostream& out) const;
    
    void save_error(int err);
    //! return error if one occured
    int get_error() const;

    //! Rises an exception if there was an error with the I/O.
    bool check_error();

protected:
    void check_nref(bool after = false);

private:
    void check_nref_failed(bool after);

    Request(const Request&);
    Request& operator = (const Request&);
  

public:
    enum State { 
            OP = 0, 
            DONE = 1, 
            READY2DIE = 2 
    };

    void wait(bool measure_time = true);

    virtual bool cancel();
    bool poll();
    const char * io_type() const;

protected:
    virtual void completed(int err, bool canceled);
protected:
    static const bool debug = false;
    
    CompletionHandler _on_complete;
    int               _saved_error;
protected:
    FileBasePtr      _file;
    void*            _buffer;
    offset_type      _offset;
    size_type        _bytes;
	size_type        _doneBytes;
    ReadOrWriteType  _type;

private:
    SharedState<State>         _state;
	bool                       _sucess;
};

std::ostream& operator << (std::ostream& out, const Request& req);

///
///inlines
///
inline const FileBasePtr& Request::file() const 
{ 
    return _file; 
}
inline void * Request::buffer() const 
{ 
    return _buffer; 
}
inline Request::offset_type Request::offset() const 
{ 
    return _offset; 
}
inline Request::size_type Request::bytes() const 
{ 
    return _bytes; 
}

inline Request::size_type Request::donebytes() const 
{ 
    return _doneBytes; 
}

inline Request::ReadOrWriteType Request::type() const 
{ 
    return _type; 
}

inline void Request::check_nref(bool after)
{
    RequestPtr ptr= shared_from_this();
    if (ptr.unique()) {
        check_nref_failed(after);
    }
}

inline bool Request::check_error() {
    if (_saved_error)
        return false;
    return true;
}

inline void Request::save_error(int err) 
{
     _saved_error = err;
}
    //! return error if one occured
inline int Request::get_error() const 
{ 
    return _saved_error; 
}

} //namespace fernmat

#endif

